package com.sky.haikangdemo.utils;

import cn.hutool.core.collection.CollectionUtil;
import com.sky.haikangdemo.config.PlateConfig;
import com.sky.haikangdemo.config.PublicStartConfig;
import com.sky.haikangdemo.entity.Gate;
import com.sky.haikangdemo.mapper.GateMapper;
import com.sky.haikangdemo.hiksdk.HCNetSDK;
import com.sky.haikangdemo.service.MsgCallBackService;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author yts
 * @description: TODO 海康摄像头初始化具体实现
 */
@Component
@Slf4j
public class HikVisionSDKInitUtil {
    @Autowired
    private GateMapper gateMapper;

    static HCNetSDK hCNetSDK = null;
    /**
     * 多个设备（ user ）
     */
    static ConcurrentHashMap<String, Integer> lUserIDMap = new ConcurrentHashMap<>();
    /**
     * 多个设备（ alarm handle ）
     */
    static ConcurrentHashMap<String, Integer> lAlarmHandleMap = new ConcurrentHashMap<>();
    /**
     * 是否已经设置报警回调
     */
    static boolean bAlarmSet = false;

    /**
     * 海康车牌摄像头初始化
     */
    public void init() {
        plateNumberInit();
    }

    /**
     * 海康车牌摄像头初始化
     */
    public void plateNumberInit() {
        if (hCNetSDK == null) {
            if (!createSDKInstance()) {
                log.info("[Hikvision Initialize] Hikvision SDK loading failed");
                return;
            }
            log.info("[Hikvision Initialize] Hikvision SDK loading successful");
        }
        // 初始化
        if (!hCNetSDK.NET_DVR_Init()) {
            log.info("[Hikvision Initialize]  Initialization failed");
            return;
        }
        log.info("[Hikvision Initialize] Initialization success");
        // 设置连接时间与重连时间
        connect(hCNetSDK);

        // TODO 循环登陆注册多个设备信息
        List<Gate> deviceList = PublicStartConfig.gates;
        if (CollectionUtil.isEmpty(deviceList)) {
            List<Gate> gates = gateMapper.selectAll();
            if (CollectionUtil.isNotEmpty(gates)) {
                deviceList = PublicStartConfig.gates = gates;
            }
        }
        for (Gate gate : deviceList) {
            Integer lUserId = login(hCNetSDK, gate.getDeviceIp(), PlateConfig.username, PlateConfig.password, PlateConfig.devicePort);
            log.info("login Device User ID: {}", lUserId);
            if (lUserId < 0) {
                log.info("[Hikvision Initialize] {}:{} Login failed", gate.getDeviceIp(), PlateConfig.devicePort);
                return;
            }
            String key = gate.getDeviceIp() + PlateConfig.devicePort + PlateConfig.username + PlateConfig.password;
            lUserIDMap.put(key, lUserId);
            // 获取设备基本信息
            device(lUserId);
            // 设置报警回调函数
            if (!bAlarmSet) {
                setDefenseListener();
            }

            // 启用布防 建立报警上传通道
            setupAlarm(lUserId);
        }
        log.info("[Hikvision Initialize] Login success,ips:{}", deviceList.stream().map(Gate::getDeviceIp).collect(Collectors.joining(",")));
    }


    /**
     * 获取led屏幕内容
     */
//    public static void getLedScreenContent() {
//        HCNetSDK.NET_DVR_LEDDISPLAY_CFG getLedContent = new HCNetSDK.NET_DVR_LEDDISPLAY_CFG();
//        HCNetSDK.NET_DVR_STD_CONFIG netDVRStdConfig = new HCNetSDK.NET_DVR_STD_CONFIG();
//        IntByReference intByReference = new IntByReference(1);
//        netDVRStdConfig.lpCondBuffer =  intByReference.getPointer();
//        netDVRStdConfig.dwCondSize = 4;
//        netDVRStdConfig.lpOutBuffer = getLedContent.getPointer();
//        netDVRStdConfig.dwOutSize = getLedContent.size();
//        netDVRStdConfig.lpInBuffer = Pointer.NULL;
//        netDVRStdConfig.dwInSize = 0;
//        netDVRStdConfig.write();
//
//        String str = new String(getLedContent.sDisplayInfo, StandardCharsets.UTF_8);
//
//        boolean b = hCNetSDK.NET_DVR_GetSTDConfig(lUserID, HCNetSDK.NET_DVR_GET_LEDDISPLAY_CFG, netDVRStdConfig);
//        if (b == false) {
//            log.info("获取led屏内容失败{}",hCNetSDK.NET_DVR_GetLastError());
//        }
//        log.info("获取led屏内容成功{}",getLedContent.sDisplayInfo);
//    }

    /**
     * 设置报警回调函数
     */
    public void setDefenseListener() {
        bAlarmSet = Boolean.TRUE;
        log.info("[Hikvision Initialize] Start setting alarm callback function");
        hCNetSDK.NET_DVR_SetDVRMessageCallBack_V31(MsgCallBackService::commonMsgCallBack, Pointer.NULL);
    }


    /**
     * 获取设备信息
     *
     * @return
     */
    public static String device(Integer lUserID) {
        HCNetSDK.NET_DVR_DEVICECFG_V40 netDVRDeviceCfg_V40 = new HCNetSDK.NET_DVR_DEVICECFG_V40();
        IntByReference intByReference = new IntByReference(netDVRDeviceCfg_V40.size());
        boolean dvrGetDVRConfig = hCNetSDK.NET_DVR_GetDVRConfig
                (lUserID, HCNetSDK.NET_DVR_GET_DEVICECFG_V40, 0, netDVRDeviceCfg_V40.getPointer(), netDVRDeviceCfg_V40.size(), intByReference);
        if (!dvrGetDVRConfig) {
            log.info("获取设备信息失败 {}", hCNetSDK.NET_DVR_GetLastError());
        }
        netDVRDeviceCfg_V40.read();
        log.info("获取设备名称：{}", new String(netDVRDeviceCfg_V40.sDVRName).trim());
        log.info("获取设备id：{}",netDVRDeviceCfg_V40.dwDVRID);
        log.info("获取设备序列号：{}",Arrays.copyOf(netDVRDeviceCfg_V40.sSerialNumber, netDVRDeviceCfg_V40.sSerialNumber.length));
        log.info("获取设备通道个数：{}",netDVRDeviceCfg_V40.byChanNum);
        log.info("获取设备通道号：{}",netDVRDeviceCfg_V40.byStartChan);
        log.info("获取设备型号：{}",netDVRDeviceCfg_V40.wDevType);
        log.info("获取设备数字通道号：{}",netDVRDeviceCfg_V40.byHighIPChanNum);
        log.info("获取设备产线：{}",netDVRDeviceCfg_V40.wDevClass);

        return String.valueOf(netDVRDeviceCfg_V40.dwDVRID);
    }


    /**
     * 动态库加载
     */
    private boolean createSDKInstance() {
        try {
            if (hCNetSDK == null) {
                synchronized (HCNetSDK.class) {
                    String path;
                    if (OsSelectUtil.isWindows()) {
                        path = "\\src\\main\\resources\\hikvision\\HCNetSDK.dll";
                    } else {
                        path = "/libhcnetsdk.so";
                    }
                    log.info("[Hikvision Initialize] LOAD_PATH: {}", System.getProperty("user.dir") + path);
                    hCNetSDK = (HCNetSDK) Native.loadLibrary(System.getProperty("user.dir") + path, HCNetSDK.class);
                }
            }
            return true;
        } catch (Exception e) {
            log.error("[Hikvision Initialize] Failed to load Hikvision SDK", e);
            return false;
        }
    }

    /**
     * 设置链接事件与重连时间
     */
    private void connect(HCNetSDK hCNetSDK) {
        // 设置连接时间与重连时间
        hCNetSDK.NET_DVR_SetConnectTime(2000, 1);
        hCNetSDK.NET_DVR_SetReconnect(10000, true);
    }


    /**
     * 注册设备
     */
    public static Integer login(HCNetSDK hCNetSDK, String deviceIp, String username, String password, Short port) {
        // 设备信息, 输出参数
        HCNetSDK.NET_DVR_DEVICEINFO_V40 m_strDeviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V40();
        HCNetSDK.NET_DVR_USER_LOGIN_INFO m_strLoginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO();
        // 注册设备-登录参数，包括设备地址、登录用户、密码等
        m_strLoginInfo.sDeviceAddress = new byte[HCNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN];
        System.arraycopy(deviceIp.getBytes(), 0, m_strLoginInfo.sDeviceAddress, 0, deviceIp.length());
        m_strLoginInfo.sUserName = new byte[HCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN];
        System.arraycopy(username.getBytes(), 0, m_strLoginInfo.sUserName, 0, username.length());
        m_strLoginInfo.sPassword = new byte[HCNetSDK.NET_DVR_LOGIN_PASSWD_MAX_LEN];
        System.arraycopy(password.getBytes(), 0, m_strLoginInfo.sPassword, 0, password.length());
        m_strLoginInfo.wPort = port;
        // 是否异步登录：0- 否，1- 是
        m_strLoginInfo.bUseAsynLogin = false;
        m_strLoginInfo.write();
        // 设备信息, 输出参数
        int lUserID = hCNetSDK.NET_DVR_Login_V40(m_strLoginInfo, m_strDeviceInfo);
        if (lUserID < 0) {
            log.info("[Hikvision Initialize] {}:{} Device registration failed: {}", deviceIp, port, hCNetSDK.NET_DVR_GetLastError());
            hCNetSDK.NET_DVR_Cleanup();
            return -1;
        }

        return lUserID;
    }


    /**
     * 启用布防
     */
    private void setupAlarm(Integer lUserID) {
        // 启用布防
        HCNetSDK.NET_DVR_SETUPALARM_PARAM lpSetupParam = new HCNetSDK.NET_DVR_SETUPALARM_PARAM();
        lpSetupParam.dwSize = 0;
        // 布防优先级：0- 一等级（高），1- 二等级（中）
        lpSetupParam.byLevel = 1;
        // 上传报警信息类型: 0- 老报警信息(NET_DVR_PLATE_RESULT), 1- 新报警信息(NET_ITS_PLATE_RESULT)
        lpSetupParam.byAlarmInfoType = 1;
        int lAlarmHandle = hCNetSDK.NET_DVR_SetupAlarmChan_V41(lUserID, lpSetupParam);
        if (lAlarmHandle < 0) {
            log.info("[Hikvision Initialize] Enabling defense failed: {}", hCNetSDK.NET_DVR_GetLastError());
            hCNetSDK.NET_DVR_Logout(lUserID);
            hCNetSDK.NET_DVR_Cleanup();
            return;
        }
        log.info("[Hikvision Initialize] Enabling defense success");
    }


    /**
     * 回调
     */
    public boolean callback(int lCommand, HCNetSDK.NET_DVR_ALARMER pAlarmer, Pointer pAlarmInfo, int dwBufLen, Pointer pUser) {
        // 设置回调方法
//        MsgCallBackService.MsesGCallBack(lCommand, pAlarmer, pAlarmInfo, dwBufLen, pUser);
        return true;
    }

    /**
     * 远程抬杆
     *
     * @param key
     * @param liftRodType ：0- 关闭道闸，1- 开启道闸，2- 停止道闸，3- 锁定道闸，4- 解锁道闸
     */
    public static void remoteControl(String key, int liftRodType) {
        Integer lUserID = lUserIDMap.get(key);
        if (hCNetSDK == null || lUserID == null) {
            log.error("远程抬杆失败，未找到对应的设备");
            return;
        }
        HCNetSDK.NET_DVR_BARRIERGATE_CFG netDvrBarriergateCfg = new HCNetSDK.NET_DVR_BARRIERGATE_CFG();
        netDvrBarriergateCfg.dwSize = netDvrBarriergateCfg.size();
        netDvrBarriergateCfg.dwChannel = 1;
        netDvrBarriergateCfg.byLaneNo = 1;
        netDvrBarriergateCfg.byBarrierGateCtrl = (byte) ((byte) liftRodType == 2 ? 0 : liftRodType);
        netDvrBarriergateCfg.byEntranceNo = 1;
        netDvrBarriergateCfg.byRes[0] = 0;
        netDvrBarriergateCfg.write();
        boolean dvrRemoteControl = hCNetSDK.NET_DVR_RemoteControl
                (lUserID, HCNetSDK.NET_DVR_BARRIERGATE_CTRL, netDvrBarriergateCfg.getPointer(), netDvrBarriergateCfg.size());
        if (!dvrRemoteControl) {
            log.info("远程控制道闸失败{}", hCNetSDK.NET_DVR_GetLastError());
        }
        switch (liftRodType) {
            case 0:
                log.info("远程关闭道闸 成功");
                break;
            case 1:
                log.info("远程开启道闸 成功");
                break;
            default:
                break;
        }

    }

    /**
     * led屏播报
     *
     * @param key
     * @param inAndOutType
     * @param content
     */
    public static void ledBroadcast(String key, String inAndOutType, String content) {
        Integer lUserID = lUserIDMap.get(key);
        if (hCNetSDK == null || lUserID == null) {
            log.error("led屏播报失败，未找到对应的设备");
            return;
        }

        HCNetSDK.NET_DVR_STD_CONFIG netDvrStdConfig = new HCNetSDK.NET_DVR_STD_CONFIG();

        HCNetSDK.NET_DVR_LEDDISPLAY_CFG netDvrLeddisplayCfg = new HCNetSDK.NET_DVR_LEDDISPLAY_CFG();
        netDvrLeddisplayCfg.dwSize = netDvrLeddisplayCfg.size();
        netDvrLeddisplayCfg.sDisplayInfo = new byte[HCNetSDK.MAX_LED_INFO_LEN];
        byte[] contentBytes = inAndOutType.getBytes(Charset.forName("GBK"));
        System.arraycopy(contentBytes, 0, netDvrLeddisplayCfg.sDisplayInfo, 0, contentBytes.length);
        netDvrLeddisplayCfg.byDisplayMode = 2;
        netDvrLeddisplayCfg.bySpeedType = 1;
        netDvrLeddisplayCfg.byShowPlateEnable = 1;
        netDvrLeddisplayCfg.dwShowTime = 8;
        netDvrLeddisplayCfg.byRes1 = 1;
        netDvrLeddisplayCfg.write();

        IntByReference intByReference = new IntByReference(1);
        netDvrStdConfig.lpCondBuffer = intByReference.getPointer();
        netDvrStdConfig.dwCondSize = 4;
        netDvrStdConfig.lpInBuffer = netDvrLeddisplayCfg.getPointer();
        netDvrStdConfig.dwInSize = netDvrLeddisplayCfg.size();
        netDvrStdConfig.lpOutBuffer = Pointer.NULL;
        netDvrStdConfig.dwOutSize = 0;
        netDvrStdConfig.write();

        boolean ledIs = hCNetSDK.NET_DVR_SetSTDConfig(lUserID, HCNetSDK.NET_DVR_SET_LEDDISPLAY_CFG, netDvrStdConfig);

        if (!ledIs) {
            log.info("led屏设置失败{}", hCNetSDK.NET_DVR_GetLastError());
        }
        log.info("led屏设置成功");

    }


    /**
     * 道闸语音播报
     *
     * @param key
     * @param content
     */
    public static void voiceBroadcast(String key, String content) {
        Integer lUserID = lUserIDMap.get(key);
        if (hCNetSDK == null || lUserID == null) {
            log.error("道闸播报失败，未找到对应的设备");
            return;
        }
        HCNetSDK.NET_DVR_STD_CONFIG netDvrStdConfig = new HCNetSDK.NET_DVR_STD_CONFIG();

        HCNetSDK.NET_DVR_VOICEBROADCAST_CFG netDvrTmevoiceCfg = new HCNetSDK.NET_DVR_VOICEBROADCAST_CFG();
        netDvrTmevoiceCfg.sInfo = new byte[HCNetSDK.MAX_VOICE_INFO_LEN];
        byte[] contentBytes = content.getBytes(Charset.forName("GB2312"));
        System.arraycopy(contentBytes, 0, netDvrTmevoiceCfg.sInfo, 0, contentBytes.length);
        netDvrTmevoiceCfg.dwSize = netDvrTmevoiceCfg.size();
        netDvrTmevoiceCfg.byBroadcastNum = 1;
        netDvrTmevoiceCfg.byIntervalTime = 5;
        netDvrTmevoiceCfg.write();

        IntByReference lchannel = new IntByReference(1);
        netDvrStdConfig.lpCondBuffer = lchannel.getPointer();
        netDvrStdConfig.dwCondSize = 4;
        netDvrStdConfig.lpInBuffer = netDvrTmevoiceCfg.getPointer();
        netDvrStdConfig.dwInSize = netDvrTmevoiceCfg.size();
        netDvrStdConfig.lpOutBuffer = Pointer.NULL;
        netDvrStdConfig.dwOutSize = 0;
        netDvrStdConfig.write();
        boolean setSTDConfig = hCNetSDK.NET_DVR_SetSTDConfig(lUserID.intValue(), HCNetSDK.NET_DVR_SET_VOICEBROADCAST_CFG, netDvrStdConfig);
        if (!setSTDConfig) {
            log.error("【海康车牌摄像头回调】语音播报失败: {}", hCNetSDK.NET_DVR_GetLastError());
        }

        log.info("语音播报成功");

    }

    /**
     * cleanup
     */
    public void cleanup() {
        log.info("Application closed, starting to release Hikvision SDK resources...");
        // TODO 1.撤防设备
        lAlarmHandleMap.values().forEach(lAlarmHandle -> {
            hCNetSDK.NET_DVR_CloseAlarmChan(lAlarmHandle);
        });

        // TODO 2.注销设备
        lUserIDMap.values().forEach(lUserId -> {
            hCNetSDK.NET_DVR_Logout(lUserId);
        });

        // TODO 3.释放SDK资源
        hCNetSDK.NET_DVR_Cleanup();
        log.info("Hikvision SDK resource release completed");
    }


}
